﻿package src {
	
	import flash.display.BitmapData;
	import flash.display.GradientType;
	import flash.display.Graphics;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	
	/**
	 * To represent sequence.fa file
	 * @author Indrek Tamming
	 */
	public class SequenceData extends Sprite {
		
		/**
		 * An array of SequenceRow objects
		 */
		private var sequenceRows:Array;
		
		/**
		 * An array of ConservationData (track file) objects
		 */
		private var conservations:Array = new Array();
		
		/**
		 * An array of track file parameters
		 */
		private var conservationParams:Array;
		
		/**
		 * Callback to the ViewerEngine
		 */
		private var cb:ViewerEngine;
		
		/**
		 * Sequence letters (nucleotides) as BitmapData
		 */
		private var letters:BitmapData;
		
		/**
		 * Sequence data constructor
		 * @param	faFile					-	sequence.fa as String
		 * @param	cb						-	ViewerEngine
		 * @param	conservationParams		-	conservation params array
		 */
		public function SequenceData(faFile:String, cb:ViewerEngine, conservationParams:Array):void {			
			this.cb = cb;
			this.conservationParams = conservationParams;
			var faFileArray:Array = faFile.split("\n");
			var _length:int = faFileArray.length;
			sequenceRows = new Array(_length * 0.5 << 0);
			var seqRowsIndexer:int = 0;
			for (var i:int = 0; i < _length; i += 2) {
				var name:String = faFileArray[i].substring(1);
				if (!name.length > 0) continue;
				var seq:String = String(faFileArray[i + 1]);
				if (!seq.length > 0) continue;
				var seqRow:SequenceRow = new SequenceRow(name, seq);
				sequenceRows[seqRowsIndexer++] = seqRow;
			}
			if (cb.getNumberOfLettersInRow(4) > getSeqRowLength()) {
				cb.setLetterSize(4, (cb.viewWidth - 20) / getSeqRowLength());
			}
			if (cb.getNumberOfLettersInRow(3) > getSeqRowLength()) {
				cb.setLetterSize(3, (cb.viewWidth - 10) / getSeqRowLength());
			}
			drawLetterSpritesheets();
		}
		
		/**
		 * Draw spritesheets to copy letters from
		 */
		private function drawLetterSpritesheets():void {
			letters = new BitmapData(40, 15, true, 0);
			var s:Sprite = new Sprite();
			
			// Large
			var a:MovieClip = new A(); // TODO export ACGT as Sprites from library
			var c:MovieClip = new C();
			c.x = 10;
			var g:MovieClip = new G();
			g.x = 20;
			var t:MovieClip = new T();
			t.x = 30;
			s.addChild(a);
			s.addChild(c);
			s.addChild(g);
			s.addChild(t);
			
			// Small
			var sa:MovieClip = new smallA();
			var sc:MovieClip = new smallC();
			sc.x = 5;
			var sg:MovieClip = new smallG();
			sg.x = 10;
			var st:MovieClip = new smallT();
			st.x = 15;
			sa.y = sc.y = sg.y = st.y = 10;
			s.addChild(sa);
			s.addChild(sc);
			s.addChild(sg);
			s.addChild(st);
			
			letters.draw(s);
		}
		
		/**
		 * Draw all visible sequences with tracks
		 * @param	leftSymbolIndex
		 * @param	upperRowIndex
		 * @param	zoomLevel
		 */
		public function draw(leftSymbolIndex:int, upperRowIndex:int, zoomLevel:int):void {
			while (this.numChildren > 0) this.removeChildAt(0); // if redrawn, clear the area
			var g:Graphics = this.graphics;
			g.clear(); // if redrawn, clear the area
			
			g.beginFill(0xFFFFFF); // draw a white background
			g.drawRect(0, 0, cb.viewWidth, cb.viewHeight);
			g.endFill();
			
			for (var i:uint = upperRowIndex; i < upperRowIndex + cb.getNumberOfRowsOnView(zoomLevel); i++) {
				if (sequenceRows[i] == null || i < 0) continue;
				var seqRow:SequenceRow = sequenceRows[i];
				var consRows:Array = new Array(conservations.length);
				for (var j:int = 0; j < consRows.length; j++) {
					consRows[j] = conservations[j].getRow(i);
				}
				var seqRowSprite:Sprite = 
					seqRow.draw(leftSymbolIndex, zoomLevel, consRows, conservationParams, cb.getNumberOfLettersInRow(zoomLevel), cb.getLetterSize(zoomLevel), cb.viewWidth, letters);
				seqRowSprite.y = (i - upperRowIndex + 1) * cb.getSpaceBetweenRows(zoomLevel);
				this.addChild(seqRowSprite);
			}
			
			this.cacheAsBitmap = true; // remove??
		}
		
		/**
		 * Draw names for sequences
		 * @param	upperRowIndex
		 * @param	zoomLevel
		 * @return
		 */
		public function drawNames(upperRowIndex:int, zoomLevel:int):Sprite {
			var sprite = new Sprite();
			for (var i:uint = upperRowIndex; i < upperRowIndex + cb.getNumberOfRowsOnView(zoomLevel) && sequenceRows[i] != null; i++) {
				var seqRow:SequenceRow = sequenceRows[i];
				var seqRowSprite:Sprite = seqRow.drawName(zoomLevel);
				seqRowSprite.y = (i - upperRowIndex + 1) * cb.getSpaceBetweenRows(zoomLevel);
				sprite.addChild(seqRowSprite);
			}
			return sprite;
		}
		
		/**
		 * Get sequences length
		 * @return
		 */
		public function getSeqRowLength():int {
			return sequenceRows[0].getSeq().length;
		}
		
		/**
		 * Get number of sequences
		 * @return
		 */
		public function getNumberOfSeqRows():int {
			return sequenceRows.length;
		}
		
		/**
		 * Get row name by number
		 * @param	rowNumber
		 * @return
		 */
		public function getRowName(rowNumber:int):String {
			return sequenceRows[rowNumber].getName();
		}
		
		/**
		 * Add track
		 * @param	data	-	ConservationData object (can be used for all tracks not only conservation)
		 */
		public function addConservation(data:ConservationData):void {
			conservations.push(data);
			conservationParams[conservations.length - 1].maximum = data.calculateMaximum();
		}
		
	}
	
}